package icu.feigroup.config;

import icu.feigroup.types.annotation.DCCValue;
import icu.feigroup.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.PubSubMessageListener;
import org.redisson.api.RBucket;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * projectName:icu.feigroup.config
 *
 * @author 飞
 * @since 2025/2/22 14:10
 * description:
 */
@Slf4j
@Configuration
public class DCCValueBeanFactory implements BeanPostProcessor {
    private static final String BASE_CONFIG_PATH = "group_market_dcc";

    private final RedissonClient redissonClient;

    private final Map<String,Object> dccObj = new HashMap<>();

    public DCCValueBeanFactory(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Bean("dccTopic")
    public RTopic dccRTopicListener(RedissonClient redissonClient){
        RTopic topic = redissonClient.getTopic("group_market_dcc");
        topic.addListener(String.class, (charSequence, s) -> {
            String[] split = s.split(Constants.SPLIT);
            String attribute = split[0];
            String key = BASE_CONFIG_PATH+attribute;
            String value = split[1];

            RBucket<Object> bucket = redissonClient.getBucket(key);
            boolean exists = bucket.isExists();
            if (!exists) return;

            bucket.set(value);
            Object objectBean = dccObj.get(key);

            if (objectBean==null) return;

            Class<?> objectBeanClass = objectBean.getClass();

            if (AopUtils.isAopProxy(objectBeanClass)){
                objectBeanClass = AopUtils.getTargetClass(objectBeanClass);
            }
            try {
                Field field = objectBeanClass.getDeclaredField(attribute);
                field.setAccessible(true);
                field.set(objectBean,value);
                field.setAccessible(false);
            } catch (Exception e) {
                throw new RuntimeException("...");
            }
        });
        return topic;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetBeanClass = bean.getClass();
        Object targetBeanObject = bean;
        //是否为代理类,是代理类则获取原始对象
        if (AopUtils.isAopProxy(bean)) {
            targetBeanClass = AopUtils.getTargetClass(bean);
            targetBeanObject = AopProxyUtils.getSingletonTarget(bean);
        }

        Field[] fields = targetBeanClass.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(DCCValue.class)) {
                continue;
            }
            DCCValue dccValue = field.getAnnotation(DCCValue.class);
            String value = dccValue.value();
            if (StringUtils.isBlank(value)) {
                throw new RuntimeException("...");
            }

            String[] split = value.split(":");
            String key = BASE_CONFIG_PATH.concat(split[0]);
            String defaultValue = split.length == 2 ? split[1] : null;

            String setValue = defaultValue;


            try {
                if (StringUtils.isBlank(defaultValue)){
                    throw new RuntimeException("...");
                }

                RBucket<String> bucket = redissonClient.getBucket(key);
                if (!bucket.isExists()){
                    bucket.set(defaultValue);
                }else {
                    setValue = bucket.get();
                }

                field.setAccessible(true);
                field.set(targetBeanObject,setValue);
                field.setAccessible(false);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("...");
            }
            dccObj.put(key,targetBeanObject);
        }
        return bean;
    }
}
